package com.msi;

import org.simpleframework.xml.Element;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Vertex {

	/**
	 * liczba całkowita reprezentujca kolor wierchołka wartość -1 oznacza, że
	 * wiercho�ek nie zosta� jeszcze pokolorwany
	 */
	@Element
	private int color = -1;
	/**
	 * stopie� wiercho�ka
	 */
	@Element
	private int degree;
	/**
	 * ID wierzcho�ka (warto�ci od zera)
	 */
	@Element
	private int id;
	/**
	 * list� s�siedztwa precyzuj�ca kraw�dzie przylegaj�ce do bie��cego
	 * wiercho�ka
	 */
	List<Vertex> neighbourhood = new ArrayList<Vertex>();
	/**
	 * wsp�rz�dna X wierxcho�ka warto�� -1 oznacza, �e wsp�rz�dne nie zosta�y
	 * sprecyzowane, a co za tym idzie graf zostanie wy�wietlony przy u�yciu
	 * mechanizmu autolayautu
	 */
	@Element
	private double x = -1;

	/**
	 * wsp�rz�dna Y wierzcho�ka warto�� -1 oznacza, �e wsp�rz�dne nie zosta�y
	 * sprecyzowane, a co za tym idzie graf zostanie wy�wietlony przy u�yciu
	 * mechanizmu autolayautu
	 */
	@Element
	private double y = -1;

	private boolean visited = false;
	
	private boolean excluded = false;

	public Vertex(int i) {
		this.id = i;
		degree = 0;
	}

	/**
	 * @param v
	 *            wiercho�ek s�siaduj�cy
	 */
	public void addNeighbour(Vertex v) {
		addNeighbourOneWay(v);
		v.addNeighbourOneWay(this);
	}

	public void addNeighbourOneWay(Vertex vertex) {
		neighbourhood.add(vertex);
		++degree;
	}
	
	public Vertex createVertexCopyWithoutNeighbors()
	{
		Vertex result = new Vertex(id);
		result.setColor(color);
		result.setX(x);
		result.setY(y);
		if(visited)
			result.markAsVisited();
	
		return result;
	}

	private List<Vertex> createSortedVericesList() {

		sortVerticesById(neighbourhood);
		return neighbourhood;

	}

	private void delNeighbor(int id) {
		int i = 0;
		for (; i < neighbourhood.size(); ++i) {
			if (neighbourhood.get(i).getId() == id) {
				neighbourhood.remove(i);
				--degree;
				break;
			}
		}
	}

	private void delNeighbor(Vertex vertex) {
		if (neighbourhood.remove(vertex))
			--degree;
	}

	public int getColor() {
		return color;
	}

	public int getDegree() {
		return degree;
	}

	public int getId() {
		return id;
	}

	public double getY() {
		return y;
	}

	public double getX() {
		return x;
	}

	public List<Vertex> getNeighborhood() {
		return neighbourhood;
	}

	public void print() {
		List<Vertex> verticesList = createSortedVericesList();
		System.out.print("vertex id=" + getId() + " degree=" + degree
				+ "  color=" + color + "  -> ");
		for (Vertex v : verticesList) {
			System.out.print(" '" + v.getId());
		}
	}

	public void resetColor() {
		color = -1;
	}

	public void setColor(int c) {
		color = c;
	}

	public void setY(double y) {
		this.y = y;
	}

	public void setX(double x) {
		this.x = x;
	}

	private void sortVerticesById(List<Vertex> verticesList) {
		Collections.sort(verticesList, new Comparator<Vertex>() {

			@Override
			public int compare(Vertex o1, Vertex o2) {
				int id1 = o1.getId();
				int id2 = o2.getId();
				if (id1 < id2)
					return -1;
				if (id1 > id2)
					return 1;
				return 0;
			}

		});
	}

	/**
	 * Oznacza wierzchołek jako odwiedzony
	 */
	public void markAsVisited() {
		visited = true;
	}

	/**
	 * Zwraca informację czy wierzchołek został już odwiedzony
	 * 
	 * @return informacja czy wierzchołek został już odwiedzony
	 */
	public boolean isVisited() {
		return visited;
	}
	
	
	/**
	 * Oznacza wierzchołek jako wyłączony z grafu
	 */
	public void exclude(){
	    this.excluded = true;
	    // kazdemu sąsiadowi zmniejszamy stopień - ten wierzchołek już się nie wlicza
	    for(Vertex neighbour : neighbourhood){
	        --neighbour.degree;
	    }
	}
	
	
	/**
	 * Zwraca informację, czy wierzchołek jest wyłączony z grafu
	 * @return informacja, czy wierzchołek jest wyłączony z grafu
	 */
	public boolean isExcluded(){
	    return this.excluded;
	}
	

	@Override
	public boolean equals(Object obj) {
		Vertex v = (Vertex) obj;
		return id == v.id;
	}

	@Override
	public int hashCode() {
		// TODO
		return super.hashCode();
	}
}
